/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.panomedic.core;

import com.panomedic.Constants;
import com.panomedic.log4j.LogCategories;
import com.panomedic.utils.Utils;
import com.panomedic.log4j.Logger;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.File;
import java.util.prefs.Preferences;

/**
 *
 * @author Yare
 */
public class Photo {

    static Logger logger = new Logger();
    protected Preferences prefs;
    protected File file = null;
    protected String name = null;
    protected BufferedImage image = null;
    protected BufferedImage thumb = null;
    protected Raster raster = null;
    protected Histogram histogram = null;
    protected Point topLeft = null;
    protected Point bottomRight = null;
    protected Point nonBlank = null;
    protected Point center = null;
    protected Point top = null;
    protected Point left = null;
    protected Point right = null;
    protected Point bottom = null;    //protected static Dimension size = null;
    protected Dimension thumbLimit = new Dimension(150, 150);   //const
    protected boolean usedJarnik = false;
    protected int order = 0;
    protected static int maxOrder = 0;
    protected double[] shift = new double[]{0.0, 0.0, 0.0};
    protected double[] expand = new double[]{1.0, 1.0, 1.0};
    protected boolean shifted = false;
    
    protected double[] bLog = new double[]{0.0, 0.0, 0.0};
    protected double[] cLog = new double[]{1.0, 1.0, 1.0};

    public double[] getBLog() {
        return bLog;
    }

    public void setBLog(double[] bLog) {
        this.bLog = bLog;
    }

    public double[] getCLog() {
        return cLog;
    }

    public void setCLog(double[] cLog) {
        this.cLog = cLog;
    }

    

    public Photo() {
        this(null, null);
        //if (size != null) {
        image = new BufferedImage(Photos.getPhotoSize().width, Photos.getPhotoSize().height,
                BufferedImage.TYPE_INT_ARGB);
        raster = image.getData();

    //}
    }

    public Photo(BufferedImage image) {
        this(null, image);
    }

    public Photo(File file, BufferedImage image) {

        prefs = Preferences.userRoot().node(Constants.defNodePath);

        // only for getResult (outside points and thumb not necessary)
        if (image == null) {
            image = new BufferedImage(Photos.getPhotoSize().width, Photos.getPhotoSize().height,
                    BufferedImage.TYPE_INT_ARGB);
            System.out.println("Result photo created.");
        }

        if (file != null) {
            this.file = file;
            this.name = file.getName();
        } // so far for result
        else {
            this.setName("result");
        }

        this.image = image;
        raster = image.getData();
        thumb = createThumb();

        //for images with alpha channel
        if (raster.getNumBands() == 4) {
            top = findTop();
            right = findRight();
            left = findLeft();
            bottom = findBottom();

            topLeft = new Point(left.x, top.y);
            System.out.println("topLeft: " + topLeft.toString());
            bottomRight = new Point(right.x, bottom.y);
            System.out.println("bottomRight: " + bottomRight.toString());

            center = new Point(bottomRight.x - topLeft.x + 1, bottomRight.y - topLeft.y + 1);

//  buffering the image
//            int width = right.x - left.x + 1;
//            int height = bottom.y - top.y +1;
//            imageData = new float[width][height][4];
//            for( int x = 0; x < width; x++)
//                for ( int y = 0; y < height; y++)
//                {
//                    int[] px = null;
//                    px = raster.getPixel(x + topLeft.x, y + topLeft.y, px);
//                    
//                    ColorConvertor.RGBtoLAB(px);
//                    
//                }
        }


    // for white background images
//            nonBlank = findNonBlank();
//            if (nonBlank != null) {
//                System.out.println("nonBlank: "+nonBlank.toString());
//                topLeft = findTopLeft();
//                System.out.println("topLeft: "+topLeft.toString());
//                bottomRight = findBottomRight();
//                System.out.println("bottomRight: "+bottomRight.toString());
//            }




    }

    /**
     * Decides if taken intersection is all inside the photo
     * @param in
     * @return
     */
    public boolean surrounds(Intersection in) {
        if (topLeft.x <= in.getTopLeft().x && topLeft.y <= in.getTopLeft().y && bottomRight.x >= in.getBottomRight().x && bottomRight.y >= in.getBottomRight().y) {
            logger.debug(LogCategories.PROCESSING, "-----" + in + " is in " + this);
            return true;
        }
        return false;

    }

    public boolean contains(int x, int y) {
        if (topLeft.x <= x && topLeft.y <= y &&
                bottomRight.x >= x && bottomRight.y >= y) {
            if (raster.getSample(x, y, 3) == 255) {
                return true;
            }
        }
        return false;
    }

//    protected Point findNonBlank() {
//        int x;// = size.width / 2;
//        int y;// = size.height / 2;
//        
//        Point found = null;
//        int[] px = new int[4];
//        int divider = 2;
//        while ( found == null &&  divider < 2* (Math.max(size.width, size.height))) {
//            int leapX = size.width / divider;
//            int leapY = size.height / divider;
//            for ( x = 1; x < divider; x++ )
//                for (y = 1; y < divider; y++ ){
//                    
//                    raster.getPixel(x*leapX, y*leapY, px);
//                    for ( int i = 0; i < 3; i++ ) {
//                        if (px[i] != 255) {
//                            found = new Point(x*leapX,y*leapY);
//                            return found;
//                        }
//                    }
//                }
//            divider *= 2;
//        }
//        
//        if ( found != null )
//            System.out.println("There is not any image to process in: "+ file.getName());
//            
//        return found;
//    }
//
//    protected Point findTopLeft() {
//        topLeft = new Point(nonBlank);
//        int[] px = null;
//        
//        while (topLeft.x != 0) {
//            px = raster.getPixel(topLeft.x, nonBlank.y, px);
//            if (isWhitePixel(px)) {
//                topLeft.x++;
//                break;
//            }
//            topLeft.x--;
//        }
//        while (topLeft.y != 0) {
//            px = raster.getPixel(nonBlank.x, topLeft.y, px);
//            if (isWhitePixel(px)) {
//                topLeft.y++;
//                break;
//            }
//            topLeft.y--;
//        }
//        return topLeft;
//    }
//    
//    protected Point findBottomRight() {
//        bottomRight = new Point(nonBlank);
//        int[] px = null;
//        
//        while (bottomRight.x != size.width) {
//            px = raster.getPixel(bottomRight.x, nonBlank.y, px);
//            if (isWhitePixel(px)) {
//                bottomRight.x--;
//                break;
//            }
//            bottomRight.x++;
//        }
//        while (bottomRight.y != size.height) {
//            px = raster.getPixel(nonBlank.x, bottomRight.y, px);
//            if (isWhitePixel(px)) {
//                bottomRight.y--;
//                break;
//            }
//            bottomRight.y++;
//        }
//        return bottomRight;
//    }
//    
//    protected boolean isWhitePixel(int[] px) {
//        for (int i = 0; i < 3; i++) {
//            if (px[i] < 245 ) {
//                return false;
//            }
//        }
//        return true;
//    }
    private BufferedImage createThumb() {

        BufferedImage thu = null;
        if (image != null) {
            Dimension dim = Utils.getScaledSize(thumbLimit, image.getWidth(), image.getHeight());
            thu = new BufferedImage(dim.width, dim.height,
                    BufferedImage.TYPE_INT_RGB);
            Image scaledImg = image.getScaledInstance(dim.width, dim.height, Image.SCALE_SMOOTH);
            Graphics2D g2 = thu.createGraphics();
            g2.drawImage(scaledImg, null, null);
            g2.dispose();

        }
        return thu;
    }

    public BufferedImage getThumb() {
        return thumb;
    }

    public BufferedImage getImage() {
        return image;
    }

    public void setImage(BufferedImage image) {
        this.image = image;
    }

    public Point getBottomRight() {
        return bottomRight;
    }

    public Point getTopLeft() {
        return topLeft;
    }

    public Point getCenter() {
        return center;
    }

    public Raster getRaster() {
        return raster;
    }

    public void setRaster(WritableRaster raster) {
        this.raster = raster;
    }

    public boolean isUsedJarnik() {
        return usedJarnik;
    }

    public void setUsedJarnik(boolean usedJarnik) {
        this.usedJarnik = usedJarnik;
    }

    public double[] getShift() {
        return shift;
    }

    public void setShift(double[] shift) {
        this.shift = shift;
    }

    public boolean isShifted() {
        return shifted;
    }

    public void setShifted(boolean shifted) {
        this.shifted = shifted;
    }

    public double[] getExpand() {
        return expand;
    }

    public void setExpand(double[] expand) {
        this.expand = expand;
    }

    public int getOrder() {
        return order;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    public static int getMaxOrder() {
        return maxOrder;
    }

    public static void setMaxOrder(int maxOrder) {
        Photo.maxOrder = maxOrder;
    }

//    public static Dimension getSize() {
//        return size;
//    }
    @Override
    public String toString() {
        return name;
    }

    private Point findTop() {
        Point opaque = new Point();
        int[] px = null;
        for (int y = 0; y < image.getHeight(); y++) {
            for (int x = 0; x < image.getWidth(); x++) {
                px = raster.getPixel(x, y, px);
                if (Utils.isPixelOpaque(px)) {
                    opaque.x = x;
                    opaque.y = y;
//                    System.out.println("top opaque point: " + opaque);
                    return opaque;
                }
            }
        }
        return opaque;
    }

    private Point findLeft() {
        Point opaque = new Point();
        int[] px = null;
        for (int x = 0; x < image.getWidth(); x++) {
            for (int y = 0; y < image.getHeight(); y++) {
                px = raster.getPixel(x, y, px);
                if (Utils.isPixelOpaque(px)) {
                    opaque.x = x;
                    opaque.y = y;
//                    System.out.println("left opaque point: " + opaque);
                    return opaque;
                }
            }
        }
        return opaque;
    }

    private Point findRight() {
        Point opaque = new Point();
        int[] px = null;
        for (int x = image.getWidth() - 1; x >= 0; x--) {
            for (int y = 0; y < image.getHeight(); y++) {
                px = raster.getPixel(x, y, px);
                if (Utils.isPixelOpaque(px)) {
                    opaque.x = x;
                    opaque.y = y;
//                    System.out.println("right opaque point: " + opaque);
                    return opaque;
                }
            }
        }
        return opaque;
    }

    private Point findBottom() {
        Point opaque = new Point();
        int[] px = null;
        for (int y = image.getHeight() - 1; y >= 0; y--) {
            for (int x = 0; x < image.getWidth(); x++) {
                px = raster.getPixel(x, y, px);
                if (Utils.isPixelOpaque(px)) {
                    opaque.x = x;
                    opaque.y = y;
//                    System.out.println("bottom opaque point: " + opaque);
                    return opaque;
                }
            }
        }
        return opaque;
    }

    public boolean hasIntersection(Photo photo) {
        int p2tlx = photo.getTopLeft().x;
        int p2tly = photo.getTopLeft().y;
        int p2brx = photo.getBottomRight().x;
        int p2bry = photo.getBottomRight().y;

        if (p2tlx >= this.topLeft.x &&
                p2tlx <= this.bottomRight.x) {
            if (p2tly >= this.topLeft.y &&
                    p2tly <= this.bottomRight.y) {
                return true;
            }
            if (p2bry >= this.topLeft.y &&
                    p2bry <= this.bottomRight.y) {
                return true;
            }
        }
        if (p2brx >= this.topLeft.x &&
                p2brx <= this.bottomRight.x) {
            if (p2tly >= this.topLeft.y &&
                    p2tly <= this.bottomRight.y) {
                return true;
            }
            if (p2bry >= this.topLeft.y &&
                    p2bry <= this.bottomRight.y) {
                return true;
            }
        }
        p2tlx = this.getTopLeft().x;
        p2tly = this.getTopLeft().y;
        p2brx = this.getBottomRight().x;
        p2bry = this.getBottomRight().y;

        if (p2tlx >= photo.topLeft.x &&
                p2tlx <= photo.bottomRight.x) {
            if (p2tly >= photo.topLeft.y &&
                    p2tly <= photo.bottomRight.y) {
                return true;
            }
            if (p2bry >= photo.topLeft.y &&
                    p2bry <= photo.bottomRight.y) {
                return true;
            }
        }
        if (p2brx >= photo.topLeft.x &&
                p2brx <= photo.bottomRight.x) {
            if (p2tly >= photo.topLeft.y &&
                    p2tly <= photo.bottomRight.y) {
                return true;
            }
            if (p2bry >= photo.topLeft.y &&
                    p2bry <= photo.bottomRight.y) {
                return true;
            }
        }
        return false;
    }

    public boolean hasCommonOpaquePixels(Point topLeft, Point bottomRight, Photo photo) {
        Raster raster2 = photo.getRaster();
        for (int x = topLeft.x; x < bottomRight.x; x++) {
            for (int y = topLeft.y; y < bottomRight.y; y++) {
                if (raster.getSample(x, y, 3) == 255 && raster2.getSample(x, y, 3) == 255) {
                    return true;
                }
            }
        }
        return false;

    }

    public Point getInterTopLeft(Photo photo) {
        Point p = new Point();
        if (this.getTopLeft().x < photo.getTopLeft().x) {
            p.x = photo.getTopLeft().x;
        } else {
            p.x = this.getTopLeft().x;
        }
        if (this.getTopLeft().y < photo.getTopLeft().y) {
            p.y = photo.getTopLeft().y;
        } else {
            p.y = this.getTopLeft().y;
        }
        return p;
    }

    public Point getInterBottomRight(Photo photo) {
        Point p = new Point();
        if (this.getBottomRight().x > photo.getBottomRight().x) {
            p.x = photo.getBottomRight().x;
        } else {
            p.x = this.getBottomRight().x;
        }
        if (this.getBottomRight().y > photo.getBottomRight().y) {
            p.y = photo.getBottomRight().y;
        } else {
            p.y = this.getBottomRight().y;
        }
        return p;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getExtremeCount(Point topLeft, Point bottomRight, double threshold) {

//        double threshold = 0.02;
        int thresholdLvl = (int) Math.round(threshold * 255);
        int lower = 0 + thresholdLvl;
        int upper = 255 - thresholdLvl;

        int extremePointCountLow = 0;
        int extremePointCountHigh = 0;

        int[] px = null;
        for (int y = topLeft.y; y < bottomRight.y; y++) {
            for (int x = topLeft.x; x < bottomRight.x; x++) {
                px = raster.getPixel(x, y, px);
                if (Utils.isPixelOpaque(px)) {
                    double pixLum = Utils.getLumFromRGB(px);
                    if (pixLum <= lower ) 
                        extremePointCountLow++;
                    else if (pixLum >= upper)
                        extremePointCountHigh++;
                }
            }
        }

        logger.debug(LogCategories.PREPROCESS, "Extreme Points count -- " + name + ":: low: "+ extremePointCountLow + ":: high: "+ extremePointCountHigh);
        return extremePointCountLow + extremePointCountHigh;
    }
};
    
